/*-------------------------------------------------------------------------
   Copyright (C) 2010 One Laptop per Child

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any
   later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   In other words, you are welcome to use, share and improve this program.
   You are forbidden to forbid anyone else to use, share and improve
   what you give them.   Help stamp out software-hoarding!

   As a special exception, you may use this file as part of a free software
   library for the XO of the One Laptop per Child project without restriction.
   Specifically, if other files instantiate
   templates or use macros or inline functions from this file, or you compile
   this file and link it with other files to produce an executable, this
   file does not by itself cause the resulting executable to be covered by
   the GNU General Public License.  This exception does not however
   invalidate any other reasons why the executable file might be covered by
   the GNU General Public License.
-------------------------------------------------------------------------*/


#include <stdio.h>
#include "cc_compat.h"
#include "chip.h"
#include "uart.h"
#include "timer.h"
#include "io.h"
#include "sdi.h"
#include "queue.h"
#include "ec_cmd.h"
#include "updater.h"
#include "ps2.h"
#include "onewirebatt.h"
#include "battery.h"
#include "mppt.h"
#include "power.h"
#include "build.h"
#include "ac.h"
#include "ols.h"
#include "every.h"
#include "kbdwatch.h"
#include "event.h"

#define EC_API_VERSION 0x05

/*
 * Please try and keep this function small -- for sanity,
 * commands should mainly be implemented in their respective
 * modules.
 */

void sdi_do_cmd(void)
{
	unsigned char i, cnt;
	unsigned char arg_size;
	unsigned char cmd;
	unsigned int address;
	unsigned char byte;
	unsigned long arg_32b;

	cmd  = sdi_command_packet[0];
	arg_size = sdi_command_packet[1];

	if (arg_size > 5) arg_size=5;

	set_suspend_hint(0);
#ifdef NO_PS2
	kbd_watch_set_input();
#endif

	switch (cmd)
	{
		default:
			printf("unknown cmd 0x%x, arg_size %d\n", (uint)cmd, (uint)arg_size);
			sdi_cmdresp_error = ERR_BAD_COMMAND;
			break;

		case CMD_GET_API_VERSION:		// out: u8
			sdi_cmdresp_queue_add(EC_API_VERSION);
			break;

		case CMD_ECHO:
			for (cnt=0; cnt<arg_size; cnt++)
			{
				sdi_cmdresp_queue_add(SDI_CMD_ARG(cnt));
			}
			break;

		case CMD_READ_EXT_SCI_MASK:
			event_get_event_mask();
			break;

		case CMD_WRITE_EXT_SCI_MASK:
			event_set_event_mask(SDI_CMD_ARG(1) << 8 | SDI_CMD_ARG(0));
			break;

		case CMD_SET_DCON_POWER:
			power_dcon( SDI_CMD_ARG(0) );
			break;

		case CMD_RESET_EC:  // reset everything, including gpio
			PXCFG = 0x10;
			watchdog_reboot();
			break;

		case CMD_RESET_EC_SOFT:   // just the 8051
			E51_RST = 1;
			while (1) /* spin */;
			break;

		case CMD_SUSPEND_HINT:
			/* on B3 and earlier, SOC_SLEEP isn't present, so we
			 * can't really tell whether the host is in suspend.
			 * so on those machines we use this command as a
			 * hint.  */
			if (SDI_CMD_ARG(0))
			{
				set_suspend_hint(1);
				printf("suspend sync: %02x%02x%02x%02x\n",
					   (uint)SDI_CMD_ARG(4), (uint)SDI_CMD_ARG(3),
					   (uint)SDI_CMD_ARG(2), (uint)SDI_CMD_ARG(1));
			}
			break;
#if 0
			/* flash update */
		case CMD_JUMP_TO_UPDATE:
			puts("CMD not supported");
			break;

		case CMD_UPDATE_FLASH:
			puts("CMD not supported");
			break;
#endif
		case CMD_POWER_CYCLE:
			puts("power cycle command");
			power_command_powercycle();
			break;

		case CMD_POWER_OFF:
		case CMD_TURN_OFF_POWER:
			puts("power off command");
			power_command_shutdown();
			break;

#ifndef NO_PS2
			/* ps2 devices */
		case CMD_TOUCHPAD_CMD:
			ps2_cmd_touchpad_cmd();
			break;

		case CMD_KEYBOARD_CMD:
			ps2_cmd_keyboard_cmd();
			break;
#endif

			/* version and id */
		case CMD_READ_BOARD_ID:
			sdi_cmdresp_queue_add(board_rev);
			if ( board_rev == 0xa1 )
			{
				sdi_cmdresp_queue_add(3);
			}
			else
			{
				sdi_cmdresp_queue_add(BOARD_GENERATION);
			}
			break;

		case CMD_GET_FW_VERSION:
			build_reply_string(BUILD_FW_VERSION);
			break;

		case CMD_GET_FW_DATE:
			build_reply_string(BUILD_FW_DATE);
			break;

		case CMD_GET_FW_USER:
			build_reply_string(BUILD_FW_USER);
			break;

		case CMD_GET_FW_HASH:
			build_reply_string(BUILD_FW_HASH);
			break;

			/* batteries */
		case CMD_READ_BATTERY_TYPE:			// out: u8
			onewire_cmd_read_type();
			break;

		case CMD_READ_VOLTAGE:				// out: u16
			onewire_cmd_read_voltage();
			break;

		case CMD_READ_CURRENT:				// out: s16
			onewire_cmd_read_current();
			break;

		case CMD_READ_ACR:					// out: s16
			onewire_cmd_read_acr();
			break;

		case CMD_READ_BATT_TEMPERATURE:		// out: u16
			onewire_cmd_read_temperature();
			break;

		case CMD_READ_BATTERY_STATUS:		// out: u8
			onewire_cmd_read_status();
			break;

		case CMD_READ_SOC:					// out: u8
			onewire_cmd_read_soc();
			break;

		case CMD_READ_GAUGE_ID:				// out: u8 * 8
			onewire_cmd_read_gauge_id();
			break;

		case CMD_READ_GAUGE_DATA:			// in: u8 addr, out: u8 data
			onewire_cmd_read_gauge_data();
			break;

		case CMD_READ_BATT_ERR_CODE:		// out: u8
			onewire_cmd_read_error_code();
			break;

		case CMD_READ_MPPT_ACTIVE:			// out: u8
			mppt_cmd_read_active();
			break;

		case CMD_READ_MPPT_LIMIT:			// out: u8
			mppt_cmd_read_limit();
			break;

		case CMD_SET_MPPT_LIMIT:			// in: u8
			mppt_cmd_set_limit( SDI_CMD_ARG(0) );
			break;

		case CMD_DISABLE_MPPT:				// none
			mppt_cmd_enable(0);
			break;

		case CMD_ENABLE_MPPT:				// none
			mppt_cmd_enable(1);
			break;

			// During RunIn mode we want to try and discharge the battery
			// This sets the AC power input limit to as low as it can get
			// and disables charging
		case CMD_ENABLE_RUNIN_DISCHARGE:			// none
			battery_cmd_runin_discharge_enable();
			break;

		case CMD_DISABLE_RUNIN_DISCHARGE:			// none
			battery_cmd_runin_discharge_disable();
			break;

		case CMD_READ_LOCATION:
			// in: u8 type, u16 addr, u8 count, out: u8 data
		{
			address = SDI_CMD_ARG(2) << 8 | SDI_CMD_ARG(1);
			cnt = SDI_CMD_ARG(3);
			if (cnt > 16) cnt = 16;
			if ( SDI_CMD_ARG(0) == 1)
			{	/* assume that internal ram is what's wanted. */
				for (i = 0; i < cnt; i++)
					sdi_cmdresp_queue_add(*(unsigned char data *)address++);
			}
			else
			{
				for (i = 0; i < cnt; i++)
					sdi_cmdresp_queue_add(*(unsigned char xdata *)address++);
			}
		}
		break;

		case CMD_WRITE_LOCATION:			// in: u8 type, u16 addr, u8 data
		{
			address = SDI_CMD_ARG(2) << 8 | SDI_CMD_ARG(1);
			byte = SDI_CMD_ARG(3);
			if ( SDI_CMD_ARG(0) == 1)
			{	/* assume that internal ram is what's wanted. */
				*(unsigned char data *)address = byte;
			}
			else
			{
				*(unsigned char xdata *)address = byte;
			}
		}
		break;

		case CMD_READ_VIN:
			ac_cmd_read_Vin();
			break;

		case CMD_OLS_READ:
			ols_cmd_read();
			break;

		case CMD_OLS_SET_LIMITS:
			ols_cmd_set_limits(SDI_CMD_ARG(1) << 8 | SDI_CMD_ARG(0),
							   SDI_CMD_ARG(3) << 8 | SDI_CMD_ARG(2));
			break;

		case CMD_OLS_GET_LIMITS:
			ols_cmd_get_limits();
			break;

		case CMD_OLS_SET_CEILING:
			ols_cmd_set_ceiling(SDI_CMD_ARG(1) << 8 | SDI_CMD_ARG(0));
			break;

		case CMD_OLS_GET_CEILING:
			ols_cmd_get_ceiling();
			break;

		case CMD_OLS_SMT_LEDON:
			ols_cmd_smttest_ledon();
			break;

		case CMD_OLS_SMT_LEDOFF:
			ols_cmd_smttest_ledoff();
			break;

		case CMD_OLS_SMTTEST_STOP:
			ols_cmd_smttest_stop();
			break;

		case CMD_START_OLS_ASSY:
			ols_cmd_assytest_start();
			break;

		case CMD_STOP_OLS_ASSY:
			ols_cmd_assytest_stop();
			break;

		case CMD_READ_VIN_SCALED:
			ac_cmd_read_Vin_scaled();
			break;

		case CMD_READ_BAT_MIN_W:
			onewire_cmd_read_battery_minW();
			break;

		case CMD_READ_BAT_MAX_W:
			onewire_cmd_read_battery_maxW();
			break;

		case CMD_RESET_BAT_MINMAX_W:
			onewire_cmd_reset_battery_minmaxW();
			break;

		case CMD_ENABLE_WAKE_TIMER:
			timed_host_wakeup_cmd_enable(SDI_CMD_ARG(0));
			break;

		case CMD_SET_WAKE_TIMER:
			arg_32b = SDI_CMD_ARG(3);
			arg_32b <<= 8;
			arg_32b |= SDI_CMD_ARG(2);
			arg_32b <<= 8;
			arg_32b |= SDI_CMD_ARG(1);
			arg_32b <<= 8;
			arg_32b |= SDI_CMD_ARG(0);
			timed_host_wakeup_cmd_value(arg_32b);
			break;

		case CMD_ENABLE_WAKE_AUTORESET:
			timed_host_wakeup_cmd_autorepeat(SDI_CMD_ARG(0));
			break;

		case CMD_READ_HOST_SUS_PWR:
			onewire_cmd_read_host_suspend_pwr();
			break;

		case CMD_SET_USB_POWER:
			power_usb(SDI_CMD_ARG(0));
			break;

		case CMD_SET_SD2_POWER:
			power_sd2(SDI_CMD_ARG(0));
			break;


	}
}
